
#ifndef __TWEENER_H__
#define __TWEENER_H__

#define PI (3.141592f)

class cTweener
{
public:
	static float Linear(float time, float start, float distance, float duration)
	{
		return distance*time/duration + start;
	}

	class cQuad
	{
	public:
		static float In(float time, float start, float distance, float duration)
		{
			return distance*(time/=duration)*time + start;
		}

		static float Out(float time, float start, float distance, float duration)
		{
			return -distance*(time/=duration)*(time-2) + start;
		}

		static float InOut(float time, float start, float distance, float duration)
		{
			if ((time /= (duration/2.0f)) < 1.0f)
			{
				return distance/2.0f*time*time + start;
			}

			return -distance/2.0f*((time-=1.0f)*(time-2.0f)-1.0f) + start;
		}
	};

	class cCubic
	{
	public:
		static float In(float time, float start, float distance, float duration)
		{
			return distance*(time/=duration)*time*time + start;
		}

		static float Out(float time, float start, float distance, float duration)
		{
			return distance*((time=time/duration-1.0f)*time*time + 1.0f) + start;
		}

		static float InOut(float time, float start, float distance, float duration)
		{
			if ((time /= (duration/2.0f)) < 1.0f)
			{
				return distance/2.0f*time*time*time + start;
			}

			return distance/2.0f*((time-=2.0f)*time*time + 2.0f) + start;
		}
	};


	class cQuart
	{
	public:
		static float In(float time, float start, float distance, float duration)
		{
			return distance*(time/=duration)*time*time*time + start;
		}

		static float Out(float time, float start, float distance, float duration)
		{
			return -distance*((time=time/duration-1.0f)*time*time*time - 1.0f) + start;
		}

		static float InOut(float time, float start, float distance, float duration)
		{
			if ((time /= (duration/2.0f)) < 1.0f)
			{
				return distance/2.0f*time*time*time*time + start;
			}

			return -distance/2.0f*((time-=2.0f)*time*time*time - 2.0f) + start;
		}
	};

	class cQuint
	{
	public:
		static float In(float time, float start, float distance, float duration)
		{
			return distance*(time/=duration)*time*time*time*time + start;
		}

		static float Out(float time, float start, float distance, float duration)
		{
			return distance*((time=time/duration-1.0f)*time*time*time*time + 1.0f) + start;
		}

		static float InOut(float time, float start, float distance, float duration)
		{
			if ((time /= (duration/2.0f)) < 1.0f)
			{
				return distance/2.0f*time*time*time*time*time + start;
			}

			return distance/2.0f*((time-=2.0f)*time*time*time*time + 2.0f) + start;
		}
	};

	class cExpo
	{
	public:
		static float In(float time, float start, float distance, float duration)
		{
			return (time == 0.0f) ? start : distance*pow(2.0f, 10.0f*(time/duration-1.0f)) + start;
		}

		static float Out(float time, float start, float distance, float duration)
		{
			return (time == 0.0f) ? start + distance : distance*(-pow(2.0f, -10.0f*time/duration)+1.0f) + start;
		}

		static float InOut(float time, float start, float distance, float duration)
		{
			if (time == 0.0f)
			{
				return start;
			}

			if (time == duration)
			{
				return start + distance;
			}

			if ((time/=(duration/2.0f)) < 1.0f)
			{
				return distance/2.0f * pow(2.0f, 10.0f*(time-1.0f)) + start;
			}

			return distance/2.0f * (-pow(2.0f, -10.0f*(time-=1.0f))+2.0f) + start;
		}
	};

	class cCirc
	{
	public:
		static float In(float time, float start, float distance, float duration)
		{
			return -distance*(sqrt(1.0f-(time/duration)*time)-1.0f) + start;
		}

		static float Out(float time, float start, float distance, float duration)
		{
			return distance*sqrt(1.0f-(time=time/duration-1.0f)*time) + start;
		}

		static float InOut(float time, float start, float distance, float duration)
		{
			if ((time /= (duration/2.0f)) < 1.0f)
			{
				return -distance/2.0f*(sqrt(1.0f-time*time)-1.0f) + start;
			}

			return distance/2.0f*(sqrt(1.0f-(time-=2.0f)*time)+1.0f) + start;
		}
	};

	class cSine
	{
	public:
		static float In(float time, float start, float distance, float duration)
		{
			return -distance*cos(time/duration*(PI/2.0f)) + distance + start;
		}

		static float Out(float time, float start, float distance, float duration)
		{
			return distance*sin(time/duration*(PI/2.0f)) + start;
		}

		static float InOut(float time, float start, float distance, float duration)
		{
			return -distance/2.0f*(cos(PI*time/duration)-1.0f) + start;
		}
	};

	class cBack
	{
	public:
		static float In(float time, float start, float distance, float duration, float overShoot = 0.0f)
		{
			if (overShoot == 0.0f)
			{
				overShoot = 1.70158f;
			}

			return distance*(time/=duration)*time*((overShoot+1.0f)*time-overShoot) + start;
		}

		static float Out(float time, float start, float distance, float duration, float overShoot = 0.0f)
		{
			if (overShoot == 0.0f)
			{
				overShoot = 1.70158f;
			}

			return distance*((time=time/duration-1.0f)*time*((overShoot+1.0f)*time+overShoot)+1.0f) + start;
		}

		static float InOut(float time, float start, float distance, float duration, float overShoot = 0.0f)
		{
			if (overShoot == 0.0f)
			{
				overShoot = 1.70158f;
			}

			if ((time /= (duration/2.0f)) < 1.0f)
			{
				return distance/2.0f*(time*time*(((overShoot*=(1.525f))+1.0f)*time - overShoot)) + start;
			}

			return distance/2.0f*((time-=2.0f)*time*(((overShoot*=(1.525f))+1.0f)*time + overShoot)+2.0f) + start;
		}
	};

	class cElastic
	{
	public:
		static float In(float time, float start, float distance, float duration, float amplitude, float period)
		{
			if (time == 0.0f) 
			{
				return start;
			}

			if ((time /= duration) == 1.0f) 
			{
				return start + distance;
			}

			if (period != 0.0f) 
			{
				period = duration * 0.3f;
			}

			float s = 0.0f;
			if (amplitude == 0.0f || amplitude < abs(distance)) 
			{ 
				amplitude = distance; 
				s = period / 4.0f; 
			}
			else 
			{
				s = period / (2.0f * PI) * asin(distance / amplitude);
			}

			return -(amplitude*pow(2,10*(time-=1.0f)) * sin((time*duration-s)*(2.0f*PI)/period)) + start;
		}

		static float Out(float time, float start, float distance, float duration, float amplitude, float period)
		{
			if (time == 0.0f) 
			{
				return start;
			}

			if ((time /= duration) == 1.0f) 
			{
				return start + distance;
			}

			if (period != 0.0f) 
			{
				period = duration * 0.3f;
			}

			float s = 0.0f;
			if (amplitude == 0.0f || amplitude < abs(distance)) 
			{ 
				amplitude = distance; 
				s = period / 4.0f; 
			}
			else 
			{
				s = period / (2.0f * PI) * asin(distance / amplitude);
			}

			return (amplitude*pow(2,-10*time) * sin((time*duration-s)*(2.0f*PI)/period) + distance + start);
		}

		static float InOut(float time, float start, float distance, float duration, float amplitude, float period)
		{
			if (time == 0.0f) 
			{
				return start;
			}

			if ((time /= (duration/2.0f)) == 2.0f)
			{
				return start + distance;
			}

			if (period != 0.0f) 
			{
				period = duration * (0.3f*1.5f);
			}

			float s = 0.0f;
			if (amplitude == 0.0f || amplitude < abs(distance)) 
			{ 
				amplitude = distance; 
				s = period / 4.0f; 
			}
			else 
			{
				s = period / (2.0f * PI) * asin(distance / amplitude);
			}

			if (time < 1.0f)
			{
				return -0.5f*(amplitude*pow(2.0f, 10.0f*(time-=1.0f)) * sin((time*duration-s)*(2.0f*PI)/period)) + start;
			}

			return amplitude*pow(2.0f, -10.0f*(time-=1.0f)) * sin((time*duration-s)*(2.0f*PI)/period)*0.5f + distance + start;
		}
	};

	class Bounce
	{
	public:
		static float In(float time, float start, float distance, float duration)
		{
			return distance - Out(duration-time, 0.0f, distance, duration);
		}

		static float Out(float time, float start, float distance, float duration)
		{
			if ((time/=duration) < (1.0f/2.75f))
			{
				return distance*(7.5625f*time*time) + start;
			}
			else if (time < (2.0f/2.75f))
			{
				return distance*(7.5625f*(time-=(1.5f/2.75f))*time + 0.75f) + start;
			}
			else if (time < (2.5f/2.75f))
			{
				return distance*(7.5625f*(time-=(2.25f/2.75f))*time + 0.9375f) + start;
			}
			else
			{
				return distance*(7.5625f*(time-=(2.625f/2.75f))*time + 0.984375f) + start;
			}
		}

		static float InOut(float time, float start, float distance, float duration)
		{
			if (time < duration/2.0f)
			{
				return In(time*2.0f, 0.0f, distance, duration)*0.5f + start;
			}
			else
			{
				return Out(time*2.0f-duration, 0.0f, distance, duration)*0.5f + distance*0.5f + start;
			}
		}
	};
};

#endif //__TWEENER_H__